use std::{collections::{BTreeMap, HashMap, HashSet}, path::PathBuf};

use serde::{Deserialize, Serialize};
use wtask_app::WTaskSubmitMetaBase;
use wtask_base::{error::WResult, export::{Local, Value, log_debug, serde_from_value}, logs::{TIME_FORMAT_TIME, log_format}};




// machine_id -> { arg_id: arg_value }
type InpArgs = BTreeMap<String, HashMap<String, String>>;




#[derive(Debug, Clone, Serialize, Deserialize)]
struct FlowTaskMetaInp {
    id: String,
    version: String,
    doc: Value,
    args: Value,
    machines: BTreeMap<String, String>,
    tags: Value,
}


#[derive(Debug, Clone)]
pub(super) struct FlowTaskMeta {
    pub(super) id: String,
    pub(super) version: String,
    pub(super) args: InpArgs,
    pub(super) machines: HashSet<String>,
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowShellMeta {
    pub(super) cmd: String,
    pub(super) res_last_log: bool,
}



#[derive(Debug, Clone)]
pub(super) enum FlowNodeDataMeta {
    Task(FlowTaskMeta),
    Shell(FlowShellMeta),
    Arg(WTaskSubmitMetaBase),
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowNodeData {
    pub(super) name: String,
    // shell/task/..args
    pub(super) cls: String,
    // WTaskSubmitMeta/ArgsSingle/String
    pub(super) meta: Value,
    pub(super) posi: Value,
    // for task
    pub(super) args: InpArgs,
    form: Option<Value>,
}


impl FlowNodeData {
    pub(super) fn parse_meta(cls: &str, args: InpArgs, meta: Value) -> WResult<FlowNodeDataMeta> {
        let r = match cls {
            "task" => {
                let task_temp = serde_from_value::<FlowTaskMetaInp>(meta)?;
                let machine_temp = task_temp.machines.into_keys().collect::<HashSet<String>>();
                FlowNodeDataMeta::Task(FlowTaskMeta {
                    id: task_temp.id,
                    version: task_temp.version,
                    args,
                    machines: machine_temp,
                })
            },
            "shell" => {
                let shell_temp = serde_from_value::<FlowShellMeta>(meta)?;
                FlowNodeDataMeta::Shell(shell_temp)
            },
            _ => {
                let arg_temp = serde_from_value::<WTaskSubmitMetaBase>(meta)?;
                FlowNodeDataMeta::Arg(arg_temp)
            }
        };
        Ok(r)
    }
}




#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowEdgeData {
    pub(super) from: String,
    pub(super) to: String,
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowSettingData {
    pub(super) id: String,
    pub(super) name: String,
    pub(super) info: String,
    pub(super) machines: Vec<String>,
    pub(super) groups: Vec<String>,
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowData {
    pub(super) setting: FlowSettingData,
    pub(super) nodes: HashMap<String, FlowNodeData>,
    pub(super) edges: HashMap<String, FlowEdgeData>,
}








#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowSubmitData {
    // 输入参数
    // machine_id -> { node_id: arg_value }
    pub(super) args: InpArgs,
    form: Value,
}



#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowStatusResult {
    pub(super) run_id: String,
    // 开始时间
    time_start: String,
    // 结束时间
    time_done: Option<String>,
    // 是否运行成功
    success: Option<bool>,
}
impl FlowStatusResult {
    pub(super) fn running(&self) -> bool {
        self.time_done.is_none()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) enum FlowStatusNodeData {
    // 直接存储数据
    Data(String),
    // 存储machine_id，可以在FlowStatus task里面找到信息
    Task(String),
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowStatusTaskData {
    pub(super) task_id: String,
    pub(super) dir_result: HashMap<String, PathBuf>,
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowStatus {
    // 提交的form
    pub(super) submit: FlowSubmitData,
    pub(super) result: FlowStatusResult,
    // 正在运行的节点
    pub(super) running: HashSet<String>,
    // 运行完成节点的结果，None为运行错误
    pub(super) node: HashMap<String, Option<FlowStatusNodeData>>,
    // 运行完成的边的结果，是否运行成功
    pub(super) edge: HashMap<String, bool>,
    // flow编排运行的日志
    pub(super) log: Vec<String>,
    // 每个节点在不同设备上的运行结果（文件传输后相当于运行），运行成功或失败的的均在内
    // node_id -> (task_id, { machine_id: dir_result })
    pub(super) task: HashMap<String, FlowStatusTaskData>,
}

impl FlowStatus {
    pub(super) fn new(submit: FlowSubmitData, log_temp: String, run_id: &str) -> Self {
        Self {
            submit,
            result: FlowStatusResult {
                run_id: run_id.to_string(),
                time_start: Local::now().format(TIME_FORMAT_TIME).to_string(),
                time_done: None,
                success: None,
            },
            running: HashSet::new(),
            node: HashMap::new(),
            edge: HashMap::new(),
            log: vec![log_temp],
            task: HashMap::new(),
        }
    }
}





#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct FlowRunLog {
    pub(super) data: FlowData,
    pub(super) status: FlowStatus,
}


impl FlowRunLog {
    pub(super) fn done(&mut self, success: bool, log_data: Vec<String>) {
        // success
        self.status.result.success.replace(success);
        // time
        self.status.result.time_done.replace(Local::now().format(TIME_FORMAT_TIME).to_string());
        // log
        self.status.log.extend(log_data.into_iter().map(| v | {
            log_debug!("{}", v);
            log_format(v)
        }));
        // left node error
        for kk in self.status.running.clone() {
            self.status.running.remove(&kk);
            self.status.node.insert(kk, None);
        }
    }
}
